package com.bigflexlib.core.os
{



	import com.adobe.utils.ArrayUtil;
	
	import flash.utils.Dictionary;
	
	import mx.events.StateChangeEvent;
	
	import com.bigflexlib.core.os.call.ICall;
	import com.bigflexlib.core.os.thread.ThreadState;
	import com.bigflexlib.x.flash.utils.Timer.TimerUtil;
	


	
	
	
	public class RemoteTargetQueue
	{
		
		

		private var queue:Array = [ ];


		private var processing:Boolean = false;


		public function RemoteTargetQueue( )
		{
			
		}


		/**
		 * 
		 * FIFO for now.
		 * TODO: This should depend on the remote target type ( http server, local SQL database, etc )
		 * IDEA: this whole class could be run by a thread itself ?
		 * 
		 * @param call
		 * 
		 */		
		public function queueCall( call:ICall ):void
		{

			call.thread.addEventListener( StateChangeEvent.CURRENT_STATE_CHANGE, thread_currentStateChange, false, 0, true );
			
			queue.push( call );
			
			if ( ! processing )
			{
				process( );
			}
		
		}
		



		private function getNextCall( ):ICall
		{
			if ( queue.length == 0 )
				return null;
				
			
			var call:ICall;
			
			var max:int = -1;

			for each ( var c:ICall in queue )
			{
				if ( c.thread.priority > max )
				{
					call = c;
					max = c.thread.priority;
				}
			}
				
			return call;
		}

		
		

		
		private function process( ):void
		{

			var call:ICall = getNextCall( );
			if ( call == null )
			{
				processing = false;
				return;
			}

			call.addEventListener( StateChangeEvent.CURRENT_STATE_CHANGE, call_currentStateChange );
			
			processing = true;

//			call.execute( );	
	
			TimerUtil.callLater( call.execute, null, 20 ); // introduce a delay for queries that are processed too quickly
								
		}
		



		
		
		private function call_currentStateChange( event:StateChangeEvent ):void
		{
		
			var call:ICall = event.target as ICall;
			
			call.removeEventListener( StateChangeEvent.CURRENT_STATE_CHANGE, call_currentStateChange );
		
			ArrayUtil.removeValueFromArray( queue, call );
		
			process( );
		
		}
		
		


		private function thread_currentStateChange( event:StateChangeEvent ):void
		{
			
			
			if ( event.newState == ThreadState.TERMINATED )
			{
				
				
				var call:ICall;
				var toRemove:Array = [ ];
				
				for each ( call in queue )
					if ( call.thread == event.target )
						toRemove.push( call );
	
				// use two different loops to avoid eliminating from the loop target. unexpected results
	
				for each ( call in toRemove )
				{
				
					ArrayUtil.removeValueFromArray( queue, call );
				
				}
			}
			

		}












		private static var cache:Dictionary = new Dictionary( true );
		
		public static function forTarget( processingTarget:* ):RemoteTargetQueue
		{

			if ( cache[ processingTarget ] === undefined )
			{
				cache[ processingTarget ] = new RemoteTargetQueue( );
			}	
			
			return cache[ processingTarget ] as RemoteTargetQueue;
		
		}




	}


}


